package cn.tedu.justone.pack01;

/**
 * ZZ：JustOne
 * VX：Andylau2020smile
 * DY：JustOne2018
 * ZT：《》
 */
public class Demo30_Volatile增加实例变量的可见性 {
    //Volatile
    //Volatile
    //Volatile
    //Volatile简介：
    //
    // volatile在使用上有以下3个特性。
    // 1,保证可见性：B线程能马上看到A线程更改的数据。
    //   volatile可以使得在多处理器环境下保证了共享变量的可见性，那么到底什么是可见性？
    //   一般在单线程的环境下，如果向一个变量先写入一个值，然后在没有写干涉的情况下读取这个
    //   变量的值，那么这个时候读取到的这个变量的值应该是之前写入的那个值。 这本来是一件很
    //   正常的事件，但是在多线程环境下，读和写发生在不同的线程中的时候，可能会出现以下的
    //   情况：读线程不能及时的读取到其他线程写入的最新的值。这就是所谓的可见性，为了实现
    //   跨线程写入的内存可见性，必须要使用一些机制来实现，而volatile就是这样的一种机制。
    // 2,不保证原子性：
    //   所谓原子性就是不可分割，也即某个线程在做某个具体业务时，中间不可以被加塞或者分割，
    //   需要整体完整，要么同时成功 要么同时失败。volatile不保证这一点。
    // 3,禁止代码重排序。
    //
    // 下面就让我们依次对这3个特性进行论证与学习。

    //示例一：
    // 1,本案例是想演示Volatile可以增加实例变量的可见性，A线程更改的数据B线程能马上看到。
    // 2,如果当前运行的模式设置为服务器模式时(可以自行修改，方法请看图)，此代码就会出现了死循环。
    //   因为变量private boolean flag = true;分别存储在公共内存及线程的私有内存中，
    //   线程运行后在线程的私有内存中取得flag的值一直是true，而代码"mr.setFlag(false);"虽然被执行，
    //   却是将公共内存中的flag变量改成false，操作的是两块内存地址中的数据，所以一直处于死循环的状态，
    //   这个问题其实就是私有内存中的值和公共内存中的值不同步造成的，可以通过使用volatile关键字来解决，
    //   volatile的主要作用就是当线程访问flag变量时，强制地从公共内存中取值。
    //   也即是使用volatile关键字是增加了实例变量在多个线程之间的可见性。
    //   如果没有更改运行的模式为服务器模式，本案例加不加volatile效果都是一样正常。
    // 3,volatile保证数据在线程之间的可见性，但不保证同步性，也即是它不处理int i++运算操作的原子性。
    public static void main(String[] args) throws InterruptedException {
        MyRed mr = new MyRed();
        Thread t = new Thread(mr);
        t.start();
        Thread.sleep(1000);
        mr.setFlag(false);
        System.out.println("flag已经赋值为false");
    }
}


class MyRed implements Runnable{
    //看这里volatile关键字是增加了实例变量在多个线程之间的可见性
    //如果不加，则本程序无法结束。只有加了，才能看到输出"结束了run()"
    private /*volatile*/ boolean flag = true;

    public void setFlag(boolean flag){
        this.flag = flag;
    }

    @Override
    public void run() {
        System.out.println("进入了run()");
        while(flag == true){
            //书上建议这里什么都不要写，哪怕是System.out.println();也不要写
            //System.out.println("111");
        }
        System.out.println("结束了run()");
    }


}

















